package mx.com.crowdgine.util.lsystem;

/**
 * Generate a tree with a context-free L-system.
 * 
 * @author		Konrad Polthier
 * @version		04.11.01, 2.50 revised (kp) Current state m_tree converted into char[] from string.<br>
 *					30.10.01, 2.00 revised (kp) Converted into a project.<br>
 *					16.02.00, 1.00 created (kp)
 */
public class LSystem {
	
	public static final double SIERPINSKI_ANGLE = Math.toRadians(60);
	public static final int SIERPINSKI_TRIANGLE = 1;
	
	public static final double DRAGON_ANGLE = Math.toRadians(90);
	public static final int DRAGON_CURVE = 2;
	
	public static final double KOCH_ANGLE = Math.toRadians(90);
	public static final int KOCH_CURVE = 3;
	
	public static final double ORIAM_ANGLE = Math.toRadians(60);
	public static final int ORIAM_CURVE = 4;
	
	//Default config - Sierpinski Triangle
	private	char []		m_defAlphabet	= {'A', 'B', '+', '-'};
	private	String		m_defAxiom		= "A";
	private	String []	m_defRule		= {"B-A-B", "A+B+A", "+", "-"};
	
	//Dragon Curve config
	char[] dragonCurveAlphabet = {'X', 'Y', 'F', '+', '-'};
	String dragonCurveAxion = "FX";
	String[] dragonCurveRules = {"X+YF", "FX-Y", "F", "+", "-"};
	
	//Koch Curve config
	char[] kochCurveAlphabet = {'F', '+', '-'};
	String kochCurveAxion = "F";
	String[] kochCurveRules = {"F+F-F-F+F", "+", "-"};
	
	//Oriam Curve config
	char[] oriamAlphabet = {'A', 'B', '+', '-'};
	String oriamAxiom = "A";
	String[] oriamRules = {"-A-B+", "B-B+A", "+", "-"};
	
	
	/** Set of available characters. */
	private	char []		m_alphabet;
	/** Initial configuration of L-system. */
	private	String		m_axiom;
	/**
	 * Production rule of each character of the alphabet. There
	 * must exist one rule per character.
	 */
	private	String []	m_rule;
	
	
	/** Container to hold the current state of the L-system. */
	private	String		m_tree;
	
	/** Constructor. */
	public LSystem() {
		init(m_defAlphabet, m_defAxiom, m_defRule);
	}
	
	public LSystem(int configID){
		switch(configID){
			case SIERPINSKI_TRIANGLE:
				init(m_defAlphabet, m_defAxiom, m_defRule);
				break;
			case DRAGON_CURVE:
				init(dragonCurveAlphabet, dragonCurveAxion, dragonCurveRules);
				break;
			case KOCH_CURVE:
				init(kochCurveAlphabet, kochCurveAxion, kochCurveRules);
				break;
			case ORIAM_CURVE:
				init(oriamAlphabet, oriamAxiom, oriamRules);
				break;
			default:
				init(m_defAlphabet, m_defAxiom, m_defRule);
				break;
		}
	}
	
	public LSystem(char[] alphabet, String axiom, String[] rules){
		init(alphabet, axiom, rules);
	}
	
	public void init(char[] alphabet, String axiom, String[] rules) {
		m_alphabet = alphabet;
		m_axiom		= axiom;
		int numLetters = rules.length;
		m_rule = new String[numLetters];
		for (int i=0; i<numLetters; i++)
			m_rule[i] = rules[i];
		m_tree = "";
	}
	
	/**
	 * Generate the tree by applying the rules until the
	 * description has a given length.
	 * @param		maxLength		maximal length of tree until we continue to iterate.
	 */
	public void iterate(int maxLength) {
		m_tree = new String(m_axiom);
		int [] ruleLen = new int[m_alphabet.length];
		for (int j=0; j<m_alphabet.length; j++)
			ruleLen[j] = m_rule[j].length();
		for (int num=0; num<maxLength; num++) {
			int len = m_tree.length();
			int newLen = 0;
			for (int i=0; i<len; i++) {
				char c = m_tree.charAt(i);
				for (int j=0; j<m_alphabet.length; j++) {
					if (c == m_alphabet[j]) {
						newLen += ruleLen[j];
						break;
					}
				}
			}

			StringBuffer newTree = new StringBuffer(newLen);
			for (int i=0; i<len; i++) {
				char c = m_tree.charAt(i);
				for (int j=0; j<m_alphabet.length; j++) {
					if (c == m_alphabet[j]) {
						newTree.append(m_rule[j]);
						break;
					}
				}
			}
			m_tree = newTree.toString();
		}
	}
	/**
	 * Make the current state of the L-system available, e.g. for
	 * interpreting the string using turtle graphics commands.
	 */
	public String getTree() {
		return m_tree;
	}
}
